package com.mh.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.mh.common.MyWebSocketHandler;
import com.mh.dao.AuctionCourseDao;
import com.mh.dao.AuctionProductDao;
import com.mh.dao.UsersDao;
import com.mh.dto.AuctionConfirmDto;
import com.mh.dto.AuctionInfo;
import com.mh.dto.R;
import com.mh.pojo.AuctionCourse;
import com.mh.pojo.AuctionProduct;
import com.mh.pojo.Users;
import com.mh.service.AuctionCourseService;
import com.mh.service.AuctionProductService;
import com.mh.utils.AuctionTools;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.HashOperations;
import org.springframework.data.redis.core.ListOperations;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;

import java.time.LocalDateTime;
import java.util.*;

/**
 * (AuctionProduct)表服务实现类
 *
 * @author zmh
 * @since 2023-07-27 16:02:39
 */
@Service
@Slf4j
public class AuctionProductServiceImpl extends ServiceImpl<AuctionProductDao, AuctionProduct> implements AuctionProductService {


    @Autowired
    private AuctionProductDao auctionProductDao;

    @Autowired
    private AuctionCourseDao auctionCourseDao;

    @Autowired
    private RedisTemplate<String, Object> redisTemplate;

    @Autowired
    private UsersDao usersDao;

    /**
     * 查询所有状态为0或1的竞品（首页竞品列表）
     */
    @Override
    public R<List<AuctionProduct>> getAllByStatus() {
        LambdaQueryWrapper<AuctionProduct> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.in(AuctionProduct::getStatus, 0, 1);
        queryWrapper.orderByDesc(AuctionProduct::getStatus);
        return R.success(auctionProductDao.selectList(queryWrapper));
    }

    /**
     * 分页查询
     *
     * @param page 查询页数
     * @param size 一页显示条数
     * @return ·
     */
    @Override
    public R<Page<AuctionProduct>> getAllByPage(int page, int size) {
        Page<AuctionProduct> auctionProductPage = new Page<>(page, size);
        LambdaQueryWrapper<AuctionProduct> queryWrapper = new LambdaQueryWrapper<>();
        //TODO 查询条件定制

        //执行查询
        auctionProductDao.selectPage(auctionProductPage,queryWrapper);
        return R.success(auctionProductPage);
    }

    /**
     * 获取拍卖时间信息
     * @return 返回Map竞品id:竞拍时间
     */
    @Override
    public R<Map<String, Object>> getAuctionTimeInfo() {

        /*
         * 查询数据库，取出status为0或1的记录，并将记录的id取出，
         * 对比redis中的列表结构，如果不存在，将id存入redis列表，
         * 并将此记录信息存入redis，结构：id：竞拍时间
         */
        // 获取status为0或1的数据
        LambdaQueryWrapper<AuctionProduct> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.in(AuctionProduct::getStatus,0,1);
        List<AuctionProduct> auctionProducts = auctionProductDao.selectList(queryWrapper);

        // 构造id列表
        ArrayList<String> ids = new ArrayList<>();
        for (AuctionProduct auctionProduct : auctionProducts) {
            ids.add(auctionProduct.getUid());
        }

        // 检查redis中是否存在"auctionIdList"字段，判断是否为第一次添加到缓存
        String listKey = "auctionIdList";
        String hashKey = "auctionTimeHash";
        ListOperations<String, Object> redisForList = redisTemplate.opsForList();
        HashOperations<String, String, Object> redisForHash = redisTemplate.opsForHash();
        if(Boolean.TRUE.equals(redisTemplate.hasKey(listKey))){
            /*
             * redis中存在id列表，取出redis中的id列表，遍历检查
             * id列表中是否缺少数据库中的竞品记录，是，给redis进行
             * 添加。
             */
            // 取出listKey中的所有数据
            List<Object> range = redisForList.range(listKey, 0, -1);
            // 取数据库中的id列表，和redis中的id列表的 差值（removeAll）,将取出来的差值添加到redis
            ids.removeAll(range);
            redisForList.leftPushAll(listKey, ids);

            // 加入redis的Map结构【string: 【string: DateTime】】
            // 调用自定义方法，根据id差值转为Map
            Map<String, Object> readyMap = AuctionTools.constructionRedisHash(ids, auctionProducts);
            redisForHash.putAll(hashKey,readyMap);
        }else{
            // redis中没有id列表，判定为第一次缓存，全部推入redis
            // 并且将数据构造成map加入redis中， 调用自定义方法构造Map
            redisForList.leftPushAll(listKey, ids);
            Map<String, Object> stringObjectMap = AuctionTools.ListAllToMap(auctionProducts);
            redisForHash.putAll(hashKey, stringObjectMap);
        }

        // 将构造好的redis结构取出，构造成map返回给前端
        Map<String, Object> readyMapData = redisForHash.randomEntries(hashKey, auctionProducts.size());
        System.out.println(readyMapData);

        return R.success(readyMapData);
    }

    /**
     * 根据id修改竞品的拍卖状态
     * @param auctionProductId 竞品id
     * @param auctionProductStatus 竞品状态
     * @return 执行结果
     */
    @Override
    public R<String> updateAuctionProductStatus(String auctionProductId, int auctionProductStatus) {
        // 判断是否是"流拍"，如果是，需要将时间拍卖时间进行延后。
        // 获取竞品的拍卖时间，将拍卖时间延后到第二天的同一时分
        LocalDateTime nextDay = LocalDateTime.now();
        if(auctionProductStatus == 0){
            AuctionProduct auctionProduct = auctionProductDao.selectById(auctionProductId);
            LocalDateTime auctionTime = auctionProduct.getAuctionTime();
            nextDay = auctionTime.plusDays(1);
        }
        LambdaUpdateWrapper<AuctionProduct> updateWrapper = new LambdaUpdateWrapper<>();
        // 修改状态和拍卖时间
        updateWrapper.eq(AuctionProduct::getUid, auctionProductId);
        updateWrapper.set(AuctionProduct::getStatus, auctionProductStatus);
        updateWrapper.set(auctionProductStatus == 0, AuctionProduct::getAuctionTime, nextDay);
        int update = auctionProductDao.update(null, updateWrapper);
        return update > 0? R.success("修改成功"): R.error("修改失败");
    }

    /**
     * 添加触发竞拍的竞品的到期时间到redis中（用于切换竞品判断倒计时相关）
     * @param auctionProductId 竞品ID
     * @param timeStamp 成交到期时间戳
     * @return 执行结果
     */
    @Override
    public R<String> addCountDownToRedis(String auctionProductId, Long timeStamp) {
        String countDownKey = "countDownMaps";
        HashOperations<String, String, Long> redisForHash = redisTemplate.opsForHash();
        /*
         判断redis中是否存在商品的倒计时时间戳
         存在：修改为新的成交时间戳
         不存在：直接添加成交时间戳
         */
        redisForHash.put(countDownKey, auctionProductId, timeStamp);
        return R.success("添加成功");
    }

    /**
     * 根据竞品id，从redis中获取竞品的成交时间戳
     * @param auctionProductId 竞品ID
     * @return 成交的时间戳
     */
    @Override
    public R<Long> getCountDownFromRedisById(String auctionProductId) {
        String countDownKey = "countDownMaps";
        HashOperations<String, String, Long> redisForHash = redisTemplate.opsForHash();
        Long countDown = redisForHash.get(countDownKey, auctionProductId);
        return R.success(countDown);
    }

    /**
     * 将拍卖过程信息存入redis（用于拍卖计算和保证数据的安全性），并且取出一项拍卖记录
     * @param auctionInfo 拍卖信息对象
     * @return 上一回合的记录
     */
    @Override
    public R<String> addAuctionInfoToRedis(AuctionInfo auctionInfo) {
        String listKey = auctionInfo.getAuctionProductId();
        ListOperations<String, Object> redisForList = redisTemplate.opsForList();
        // 根据传入的竞品id，查询此竞品的信息
        AuctionProduct aucProduct = auctionProductDao.selectById(auctionInfo.getAuctionProductId());
        // 检查redis中是否存在过此竞品的记录
        if(redisForList.size(listKey) < 1){
            // 查询此竞品的信息，并对比传入的出价（出价 > 底价 + 加价幅度），存入redis然后返回最新价格。
            if(!AuctionTools.checkAuctionPrice(auctionInfo.getPrice(), aucProduct)){
                return R.error("发现改价，倒反天罡！");
            }
        }else{
            // 取出redis中列表的最后一个元素低底价，对比出价价格，存入redis然后返回
            LinkedHashMap<String, Object> linkedHashMap = (LinkedHashMap<String, Object>) redisForList.range(listKey, -1, -1).get(0);
            String userId = (String) linkedHashMap.get("userId");
            String auctionProductId = (String) linkedHashMap.get("auctionProductId");
            Integer price = (Integer) linkedHashMap.get("price");
            AuctionInfo auc = new AuctionInfo(userId, auctionProductId, price);
            if(auctionInfo.getPrice() < auc.getPrice() + aucProduct.getIncrementPrice()){
                return R.error("发现改价，倒反天罡！");
            }
        }
        redisForList.rightPush(listKey, auctionInfo);
        // websocket群发，返回最新竞拍价格
        MyWebSocketHandler.senAll("price" + auctionInfo.getPrice() + ",id" + auctionInfo.getAuctionProductId());
        return R.success("记录成功");
    }

    /**
     * 从redis中取出数据存入MYSQL
     * 1.修改竞品的状态（status修改为 2，owner_name为竞拍用户名）---auction_platform
     * 2.修改用户的拍卖竞拍数量 ---users
     * 3.添加redis中的拍卖过程到MySQL --- auction_course
     * @return
     */
    @Override
    public R<String> addAuctionInfoToMySQL(AuctionConfirmDto auctionConfirmDto) {
        // 取出redis中的拍卖数据
        ListOperations<String, Object> listForRedis = redisTemplate.opsForList();
        List range = listForRedis.range(auctionConfirmDto.getUid(), 0, -1);

        // 修改竞品状态和竞品所有者（竞品归属）
        LambdaUpdateWrapper<AuctionProduct> updateWrapper = new LambdaUpdateWrapper<>();
        updateWrapper.eq(AuctionProduct::getUid, auctionConfirmDto.getUid());
        updateWrapper.set(AuctionProduct::getStatus, 2);
        updateWrapper.set(AuctionProduct::getOwnerName, auctionConfirmDto.getOwnerName());
        auctionProductDao.update(null, updateWrapper);

        // 查询出用户原有的竞品持有数
        LambdaQueryWrapper<Users> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(Users::getUid, auctionConfirmDto.getUserId());
        // 暂时测试
//        queryWrapper.eq(Users::getUid, "8b801529-2c45-11ee-ad0c-02004c4f4f50");
        Users users = usersDao.selectOne(queryWrapper);
//        // 修改用户表的持有竞品数量
        LambdaUpdateWrapper<Users> userUpdateWrapper = new LambdaUpdateWrapper<>();
        userUpdateWrapper.eq(Users::getUid, auctionConfirmDto.getUserId());
        userUpdateWrapper.set(Users::getBuyingNumber, users.getBuyingNumber() + 1);

        // 从redis中取出拍卖过程信息，构造好后存入MySQL
        ArrayList<AuctionInfo> auctionInfos = new ArrayList<>();
        for (Object o : range) {
            LinkedHashMap<String, Object> o1 = (LinkedHashMap<String, Object>) o;
            String userId = (String)o1.get("userId");
            String auctionProductId = (String)o1.get("auctionProductId");
            Integer price = (Integer)o1.get("price");
            auctionCourseDao.insert(new AuctionCourse(UUID.randomUUID().toString(), userId, auctionProductId, price));
        }
        return R.success("操作成功!");
    }

    /**
     * 发布（添加记录）
     * @param auctionProduct 数据
     * @return ·
     */
    @Override
    public R<String> addAuctionProduct(AuctionProduct auctionProduct) {
        // 构造为传入的数据，填充默认字段值【uid, auctionNumber, status】
        auctionProduct.setUid(UUID.randomUUID().toString());
        auctionProduct.setAuctionNumber(0);
        auctionProduct.setStatus(0);
        // 添加竞品信息
        int insert = auctionProductDao.insert(auctionProduct);
        // 修改用户的【发布数量】，获取用户的

        if(insert == 1){
            return R.success("添加成功!");
        }else{
            return R.error("添加失败!");
        }
    }

    /**
     * 根据用户名获取用户持有的竞品
     * @param userName 用户名
     * @return ·
     */
    @Override
    public R<List<AuctionProduct>> getAllByUserName(String userName) {
        // 条件构造器，构造用户名查询
        LambdaQueryWrapper<AuctionProduct> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(AuctionProduct::getOwnerName, userName);
        return R.success(auctionProductDao.selectList(queryWrapper));
    }
}
